package cn.ratel.route.render.template;

import cn.ratel.component.Constants;
import cn.ratel.route.ContextHolder;
import cn.ratel.route.RenderProvider;
import cn.ratel.route.render.Tpl;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.cache.ConcurrentMapTemplateCache;
import com.github.jknack.handlebars.helper.StringHelpers;
import com.github.jknack.handlebars.io.ServletContextTemplateLoader;
import com.github.jknack.handlebars.io.TemplateLoader;
import com.google.common.base.Preconditions;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

public class HandlebarsRender implements RenderProvider.Render {
	public static final String TPL_CONTEXT_VARIABLE = "TPL_CONTEXT_VARIABLE";
	private static String templatePath = "/WEB-INF/view/";

	private static Handlebars handlebars = initHandlebars();

	public static void setTemplatePath(String templatePath) {
		HandlebarsRender.templatePath = templatePath;
	}

	private static Handlebars initHandlebars() {
		TemplateLoader loader = new ServletContextTemplateLoader(ContextHolder.getServletContext().get(), templatePath, ".hbs");
		Handlebars handlebars = new Handlebars(loader);
		if (!Constants.me().isDevMode()) {
			handlebars.with(new ConcurrentMapTemplateCache());
		}
		StringHelpers.register(handlebars);
		DefaultHelper.regist(handlebars);
		return handlebars;
	}

	public static void registHelper(String name, Helper<?> helper) {
		handlebars.registerHelper(name, helper);
	}

	@Override
	public void render(HttpServletRequest req, HttpServletResponse res, Object result) {
		HttpServletRequest request = ContextHolder.getRequest().get();
		HttpServletResponse response = ContextHolder.getResponse().get();
		Tpl tpl = (Tpl) result;
		Template template;
		PrintWriter writer = null;
		String tplRenderResult = null;
		try {
			template = handlebars.compile(tpl.getPath());
			Map<String, Object> tplVariable = tpl.getVariable();
			Object attribute = request.getAttribute(TPL_CONTEXT_VARIABLE);
			if (attribute != null) {
				tplVariable.putAll((Map<String, Object>) attribute);
			}
			tplRenderResult = template.apply(tplVariable);
		} catch (IOException e) {
			throw new RuntimeException("render handlebar template error : " + e.getMessage(), e);
		}

		try {
			response.setCharacterEncoding("utf-8");
			response.setContentType("text/html;charset=UTF-8");
			writer = response.getWriter();
			writer.write(tplRenderResult);
			writer.flush();
		} catch (IOException e) {
			throw new RuntimeException("render error", e);
		} finally {
			if (writer != null) {
				writer.close();
			}
		}
	}

	@Override
	public Class<?> getSupportType() {
		return Tpl.class;
	}

	enum DefaultHelper implements Helper {

		niceTime {
			@Override
			public CharSequence apply(Object context, Options options) throws IOException {
				Preconditions.checkNotNull(context, "helper:[niceTime] need one param");
				String result;
				long targetTime;
				if (context instanceof Long) {
					targetTime = (long) context;
				} else if (context instanceof Date) {
					targetTime = ((Date) context).getTime();
				} else {
					throw new RuntimeException("helper:[niceTime]'s param type must be long or date");
				}
				long ago = (System.currentTimeMillis() - targetTime) / 1000;
				if (ago < 60) {
					result = "刚刚";
				} else if (ago >= 60 && ago < 3600) {
					result = ago / 60 + "分钟前";
				} else if (ago >= 3600 && ago < 86400) {
					result = ago / 3600 + "小时前";
				} else if (ago >= 86400 && ago < 864000) {
					result = ago / 86400 + "天前";
				} else {
					result = new SimpleDateFormat("yyyy-MM-dd").format(targetTime);
				}
				return result;
			}
		};


		static void regist(Handlebars handlebars) {
			for (DefaultHelper defaultHelper : DefaultHelper.values()) {
				handlebars.registerHelper(defaultHelper.name(), defaultHelper);
			}
		}
	}
}
